home *** CD-ROM | disk | FTP | other *** search
/ boe.pres.k12.wv.us / boe.pres.k12.wv.us.zip / boe.pres.k12.wv.us / Utilities / Xerox Workcentre 5335 / Windows Scan / 64-bit_x64 / Japanese / cpsimage.cab / data / xipProcs / getOcrPage.proc < prev    next >
Text File  |  2009-04-23  |  11KB  |  304 lines

  1.  
  2. #import "fwx2xip.ucm";             // Nuance (ScanSoft) OCR support
  3. #import "ipcore2xip.ucm";          // Orient and skew from IPCore
  4. #load   "xipProcs/skewAngle.proc"; // Ease of use function for skewAngle
  5. #load "xipProcs/printImageLayers.proc";
  6.  
  7. /*
  8.  * This is needed so real rotate matches behavior of lazyRotate.
  9.  * Don't want the image width/height to be modified (other than swapping them)
  10.  */
  11. private
  12. PROCEDURE swapAspect (XIPIMAGE img, DOUBLE angle)
  13.   RETURNS (XIPIMAGE out)
  14. {
  15.    INTEGER ww = img.imageWidth;
  16.    INTEGER hh = img.imageHeight;
  17.    angle      = angle + 360.0;
  18.    INTEGER cnt    = angle/90;
  19.    DOUBLE  orient = cnt*90;
  20.    DOUBLE  skew   = angle - orient;
  21.    if (orient != 0)
  22.       img = img.rotate ( fangle: orient, incore: TRUE, nomask: TRUE ).exec();
  23.    if ( (cnt%2) == 1 )
  24.       { img.imageHeight = ww; img.imageWidth = hh; }
  25.    out = img;
  26.    if (skew != 0)
  27.       out = img.rotate ( fangle: skew, incore: TRUE, nomask: TRUE, lock: TRUE );
  28. }
  29.  
  30.  
  31. private
  32. PROCEDURE __printOCRResults (XIPIMAGE textLayer, DOUBLE orient)
  33. {
  34.    DOUBLE ucharC = textLayer.getMember (member: "ucharCount");
  35.    DOUBLE charC  = textLayer.getMember (member: "charCount");
  36.    INTEGER qcharC  = textLayer.getMember (member: "qcharCount");
  37.    INTEGER wcharC  = textLayer.getMember (member: "wordCount");
  38.  
  39.    print "orient     = " + orient;
  40.    print "charCount  = " + charC;
  41.    print "ucharCount = " + ucharC;
  42.    print "qcharCount = " + qcharC;
  43.    print "wordCount  = " + wcharC;
  44.  
  45.    if( charC == 0 ) return;
  46.    print "percent Uncertain = " + (ucharC/charC * 100);
  47. }
  48.  
  49.  
  50. /*
  51.  * Second pass at OCR if we suspect something wrong.
  52.  */
  53. private
  54. PROCEDURE getOrient4OCR (XIPIMAGE img, DOUBLE res, STRING language, INTEGER reduction, INTEGER origC)
  55.   RETURNS (DOUBLE orient, DOUBLE angle)
  56. {
  57.    // Do analysis at less than the actual image size and low res
  58.    // fwindow is in percentages, left, top, width, height
  59.    XIPIMAGE orientimg = img;
  60.  
  61.    if ( res >= 300 ) {
  62.      orientimg = orientimg.rescon (resolution: (res, 300) );
  63.      orientimg.resolution = (300, 300);
  64.    }
  65.  
  66.    INTEGER i, bestOrient;
  67.    DOUBLE  lowScore = 100.0;
  68.    DOUBLE  tmpd, ucharC, charC;
  69.    STRING  text;
  70.    XIPIMAGE textLayer;
  71.  
  72.    // Evaluate -90, 0, 90; OCR takes care of 0 and 180 automatically
  73.    for (i=-1; i<=2; i++) {
  74.      orientimg.rotate(iangle: i * 90
  75.              ).fwxOCR (texttype:"XDOC", language: language, overrideOrient: TRUE)
  76.                   Returns ( str: text, orientation: orient);
  77.      textLayer = XDOCtoXIPXML(xdoc: text);
  78.   
  79.      if (0) { // debug stuff
  80.        print "test angle = " + (i * 90);
  81.        __printOCRResults (textLayer: textLayer, orient: orient);
  82.        }
  83.    
  84.      // Get scoring
  85.      ucharC = textLayer.getMember (member: "ucharCount");
  86.      charC  = textLayer.getMember (member: "charCount");
  87.  
  88.      if(charC == 0 || charC < .2*origC) continue;
  89.      tmpd = ucharC/charC * 100;
  90.  
  91.      if ( lowScore > tmpd )
  92.        { lowScore = tmpd; bestOrient = i * 90; }
  93.  
  94.      // If less than 1% it's probably good
  95.      // if ( lowScore < 1.0 ) break;
  96.    }
  97.    
  98.    orient = bestOrient;
  99. }
  100.  
  101.  
  102. /*
  103.  * Orient the image and perform OCR
  104.  */
  105. private
  106. PROCEDURE process4OCRLayer (XIPIMAGE img, DOUBLE orient, DOUBLE corr,
  107.                               STRING language, INTEGER tw, INTEGER th, BOOLEAN override)
  108.   RETURNS ( XIPIMAGE out, INTEGER ocrOrient, XIPIMAGE textLayer )
  109. {
  110.    if ( orient || corr > 0.1 || corr < -0.1 ) {
  111.      if (orient == 90 || orient == -90)
  112.        img = swapAspect (angle: corr, img: img).exec();
  113.      else {
  114.        img = img.rotate (fangle: corr, incore: TRUE, nomask: TRUE, lock: TRUE).exec();
  115.        }
  116.      }
  117.  
  118.    // default to english if not specified
  119.    if ( !language ) language = "English";
  120.    STRING text;
  121.    img.fwxOCR (texttype:"XDOC", language: language, overrideOrient: override)
  122.       Returns ( str: text, orientation: ocrOrient);
  123.  
  124.    // Did we find anything, if not no text on page
  125.    XIPIMAGE tmpT = XDOCtoXIPXML(xdoc: text);
  126.    if ( tmpT.getMember (member:"wordCount") && tmpT.getMember (member:"charCount") )
  127.      textLayer = tmpT;
  128.  
  129.    out = img;
  130. }
  131.  
  132.  
  133. /* @getOcrPage
  134.   // DESCRIPTION
  135.   Takes an input XIPIMAGE and returns a XIPIMAGE with XIPLAYER layerType XIP_Text.
  136.   The input can be single to Nlayer. If Nlayer all mask channels are used to
  137.   assemble an image for OCR.  Orientation and deskew are done before OCR.  This gives
  138.   the highest quality of recognition.
  139.   
  140.   The image for OCR is converted to binary before orientation and deskew to minimze
  141.   memory use.
  142.   
  143.   // ARGUMENTS
  144.   XIPIMAGE  input        Input image to use for OCR
  145.   STRING    language     Language used for recognition, default is English
  146.   INTEGER   reduction    The reduce argument is the factor by which the binary image will
  147.                          be reduced. Factor can be: 1, 2, 4, 8, 16, or 32. The default
  148.                          is 2 if not specified. The reduction factor determines the step
  149.                          size used in the binary search for fine tuning the estimated skew
  150.                          angle.
  151.   BOOLEAN   overrideOrient  If you know the primary orientation you can override the automatic
  152.                          detection and correction; will process a little faster as well.
  153.   BOOLEAN   visibility   Text visibility control, default is FALSE (off).  If TRUE will show
  154.                          red text in document, e.g., PDF.
  155.   
  156.   // RETURNS
  157.   DOUBLE    angle        Angle used to correct input image.  The XIP_Text layer
  158.                          will be soft corrected to handle this offset.  In other
  159.                          words if the image 10 degrees off and ends up in a PDF
  160.                          container with searchable text, the image and text will be
  161.                          display at 10 degrees.
  162.   XIPIMAGE  textLayer    The returned OCR data in a XIP_Text layer type
  163. */
  164.  
  165. PROCEDURE getOcrPage (XIPIMAGE input,
  166.                       STRING   language,
  167.                       INTEGER  reduction,
  168.                       BOOLEAN  overrideOrient,
  169.                       BOOLEAN  visibility)
  170.   RETURNS (XIPIMAGE textLayer, DOUBLE angle)
  171. {
  172.    // Loop through all layers getting list of images and normalized offsets
  173.    ELFLIST imgs;      // list of images to be merged
  174.    ELFLIST posList;   // list of x,y merge positions
  175.    INTEGER i;
  176.    STATUS  status;
  177.    XIPIMAGE tmp, tmpT;
  178.    INTEGER ltype, anyMask;
  179.    DOUBLE OCRThreshold;
  180.  
  181.  
  182.    // Set parameters to combine all colormasks from segmented image
  183.    for ( i=0; i<input.nlayers; i++ ) {
  184.       ltype = input.getMember (member:"layerType", num: i);
  185.       if (ltype == XIP_ColorMask || ltype == XIP_ContoneMask) {
  186.          tmp = input.getLayer(num:i).unCompress();
  187.  
  188.          // Add mask layer to accomodate image overlaps
  189.          tmp = tmp.channel (command: ("Insert image 1 Mask A"), input: (tmp) );
  190.          imgs.insert ( obj: tmp);
  191.  
  192.          posList.insert (obj:input.getMember(num:i,member:"ypos") / input.imageHeight);
  193.          posList.insert (obj:input.getMember(num:i,member:"xpos") / input.imageWidth);
  194.       }
  195.    }
  196.  
  197.    STRING res = "Origin:resolution:" + input.resolution[0];
  198.    if (input.nlayers > 1 && imgs) {
  199.       // Create a background plane and merge all layers onto it
  200.       try {
  201.         tmp = pattern (constant:(0),
  202.                           space: "klinear",
  203.                            size: (input.imageWidth, input.imageHeight)
  204.             ).convert (to: (1)
  205.             ).nmerge (input: imgs,
  206.                     foffset: posList
  207.             ).modheader (modify: ("Photometry:name:graylinear")
  208.             ).modheader (modify: (res)
  209.             ).paper (
  210.             ).invert ();
  211.       } catch {
  212.         print status.message; end;
  213.       }
  214.    }
  215.    else {
  216.       if( input.getMember (member:"bits") != 1 )
  217.           tmp = input.unCompress().channel(parts: "1").invert()
  218.                .binarize2(expansion:0, gradThresh:2).invert();
  219.       else
  220.           tmp = input;
  221.    }
  222.  
  223.    // Resolve the binary image to find and correct angle
  224.    tmp = tmp.exec();
  225.  
  226.    INTEGER tw = tmp.imageWidth;
  227.    INTEGER th = tmp.imageHeight;
  228.  
  229.    // get quick orientation
  230.    DOUBLE orient;
  231.    if (overrideOrient == FALSE) {
  232.      // Get the text only
  233.      tmp.b2TextOnly()
  234.        Returns ( mask: anyMask, img: tmpT);
  235.  
  236.      orient = tmpT.orient();
  237.      if (orient == 180) orient = 0;
  238.      else if (orient == 270) orient = -90;
  239.      }
  240.  
  241.  
  242.    // Get skewAngle
  243.    // fwindow is in percentages, left, top, width, height
  244.    // NOTE: IPCore treats plus angles as clockwise, this is counter to XEngine rotates
  245.    if ( input.resolution[0] == 400 )
  246.      angle = skewAngle ( img: tmp.rescon (resolution: (input.resolution[0], 200)),
  247.                       orient: orient, type: "binary", reduction: reduction );
  248.    else if ( input.resolution[0] >= 300 )
  249.      angle = skewAngle ( img: tmp.rescon (resolution: (input.resolution[0], 150)),
  250.                       orient: orient, type: "binary", reduction: reduction );
  251.    else
  252.      angle = skewAngle ( img: tmp, orient: orient, type: "binary", reduction: reduction );
  253.  
  254.  
  255.    DOUBLE corr = -1 * angle + orient;
  256.    tmpT = tmp;
  257.  
  258.    // Is there anything at all, if not we're done
  259.    process4OCRLayer (img: tmpT, orient: orient, corr: corr, language: language, tw: tw, th: th,
  260.                 override: overrideOrient) Returns (ocrOrient: orient, textLayer: textLayer);
  261.    if (0) __printOCRResults (textLayer: textLayer, orient: orient);
  262.  
  263.    if ( !textLayer )
  264.      return;
  265.  
  266.    // Is it really ok or do we need to post check the orientation
  267.    DOUBLE ucharC = textLayer.getMember (member: "ucharCount");
  268.    DOUBLE charC  = textLayer.getMember (member: "charCount");
  269.  
  270.    // If no text detected, we're done and should return
  271.    if ( charC == 0 ) return;
  272.  
  273.    DOUBLE percentUncertain = ucharC / charC * 100.0;
  274.    DOUBLE cc = charC;
  275.    OCRThreshold = (cc.sqrt() / charC ) * 100;
  276.    
  277.    // OCR will self correct the orientation if it's 180 off
  278.    if ( percentUncertain > OCRThreshold ) {
  279.       getOrient4OCR (img: tmpT, res: input.resolution[0], language: language, reduction: reduction, origC: charC)
  280.          Returns ( orient: orient ); 
  281.  
  282.       corr = -1 * angle + orient;
  283.       process4OCRLayer (img: tmpT, orient: orient, corr: corr, language: language,
  284.                          tw: tw, th: th, override: TRUE)
  285.                Returns (ocrOrient: orient, textLayer: textLayer);
  286.       if ( !textLayer )
  287.         return;
  288.       }
  289.  
  290.    // print "angle = " + angle + ", orient = " + orient + ", corr = " + corr;
  291.    if( corr != 0 || orient != 0 )
  292.      textLayer = textLayer.setTransform (op: "rotate", angle: corr + orient);
  293.  
  294.    textLayer.setAttr (name: "_DIOOrigViewport", num: 0,
  295.      obj: (0.0, 0.0, input.imageWidth, input.imageHeight));
  296.  
  297.    // Debug: will set text visible
  298.    if (visibility)
  299.       textLayer.setMember (member: "visibility", value: TRUE);
  300.  
  301.    // overall correction angle
  302.    angle = -1 * angle + orient;
  303. }
  304.